#!/usr/bin/python3
# -*- coding:utf-8 -*-
# @auth:  spacelacc
# @date:  2025/04/14
# @func:  Custom tableviews for 'data confirm', 'data shown'
# @vers:  V1


import os
import re
from PySide6.QtGui     import QPainter, QPen, QColor, QAction
from PySide6.QtCore    import Signal
from PySide6.QtCore    import Qt, QModelIndex, QPoint
from PySide6.QtCore    import QAbstractListModel
from PySide6.QtWidgets import QListView, QStyledItemDelegate
from PySide6.QtWidgets import QFileDialog
 
from PySide6.QtWidgets import QAbstractItemView, QMenu
from PySide6.QtWidgets import QApplication, QWidget, QFrame

from operdialog        import AddPrefixDialog



class FileListModel(QAbstractListModel):
    def __init__(self, files:list=[]):
        super().__init__()
        self._data = files

    def rowCount(self, parent=QModelIndex()):
        return len(self._data)

    def data(self, index, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
#           return self._data[index.row()]
            return os.path.basename(self._data[index.row()])
        return None

    def setData(self, index, value, role=Qt.EditRole):
        if not index.isValid():
            return False
        if role == Qt.EditRole:
            self._data[index.row()] = value
            self.dataChanged.emit(index, index)
        return True

    def listErase(self):
        self.beginResetModel()
        self.endResetModel()

    # Update means clear and reset data
#   def listRenew(self, files:list):
#       pass

#   def listEnroll(self, files:list):
#       if not files:
#           return
#       ncnts = len(files)
#       nrows = len(self._data)
#       self.beginInsertRows(QModelIndex(), ncnts, ncnts+nrows-1)
#       self._data.extend(files)
#       self.endInsertRows()

    def listUpdate(self):
        self.beginResetModel()
        self._data.sort()
        self.endResetModel()

class FileListDelegate(QStyledItemDelegate):
    def __init__(self):
        super().__init__()
        self.linehover = -1
        self.chosen = []
        self.colorHover = QColor(0, 143, 191)
        self.colorClick = QColor(255, 110, 170)
        self.colorBase  = QColor(240, 240, 240)

    def onMouseHover(self, line):
        self.linehover = line

    def onMouseLeave(self):
        self.linehover = -1

    def onMouseClick(self, lines):
        self.chosen.clear()
        for line in lines:
            self.chosen.append(line)

    def clearconditions(self):
        self.chosen.clear()
        self.linehover = -1

    def paint(self, painter, option, index):
        painter.save()

        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        painter.setRenderHint(QPainter.RenderHint.TextAntialiasing)

#       if option.state & QStyle.State_Selected:
#           painter.fillRect(option.rect, option.palette.highlight())
#       elif option.state & QStyle.State_MouseOver:
#           painter.fillRect(option.rect, option.palette.highlight())
#       else:
#           painter.fillRect(option.rect, option.palette.base())

        painter.setPen(QPen(Qt.black, 2))
        # 1. Do 'Mouse Hover' background color processing
        if index.row() == self.linehover:
            painter.fillRect(option.rect, self.colorHover)
        else:
            painter.fillRect(option.rect, self.colorBase)

        # 2. Do 'Mouse Click Select' background/foreground color processing
        if self.chosen and index.row() in self.chosen:
            painter.fillRect(option.rect, self.colorClick)

        value = index.data()
        lbrect = option.rect.adjusted(10, 0, 0, 0)
        painter.drawText(lbrect, Qt.AlignLeft|Qt.AlignVCenter, value)

        painter.restore()


class FileListView(QListView):
    # Defines signals here
    signalMouseHover = Signal(int)
    signalMouseLeave = Signal()
    signalMouseClick = Signal(list)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.chosen = []
        self.recorder = -1
        self.previous = -1
        self.modifier = Qt.NoModifier
        self.assembler = []
        self.cwd = os.path.dirname(os.path.abspath(__file__))
        self.model = FileListModel(self.assembler)
        self.delegate  = FileListDelegate()

        self.setModel(self.model)
        self.setItemDelegate(self.delegate)
        self.setupContext()
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.popupContext)
        self.setMouseTracking(True)

        # Connecting signals
        self.signalMouseHover.connect(self.delegate.onMouseHover)
        self.signalMouseLeave.connect(self.delegate.onMouseLeave)
        self.signalMouseClick.connect(self.delegate.onMouseClick)

    def popupContext(self, pos):
        self.context.exec(self.mapToGlobal(pos))

    def setupContext(self):
        self.context = QMenu(self)
        add_files = QAction('Update Files', self)
        add_folder = QAction('Update Folder', self)
        add_prefix = QAction('Add Prefix', self)
        add_postfix = QAction('Add Postfix', self)
        alt_prefix = QAction('Alter Prefix', self)
        alt_postfix = QAction('Alter Postfix', self)
        alt_serial  = QAction('Alter Serial', self)
        clear_list  = QAction('Clear List', self)

        self.context.addAction(add_files)
        self.context.addAction(add_folder)
        self.context.addSeparator()
        self.context.addAction(add_prefix)
        self.context.addAction(add_postfix)
        self.context.addSeparator()
        self.context.addAction(alt_serial)
        self.context.addSeparator()
        self.context.addAction(alt_prefix)
        self.context.addAction(alt_postfix)
        self.context.addSeparator()
        self.context.addAction(clear_list)

        add_files.triggered.connect(self.update_list_files)
        add_folder.triggered.connect(self.update_list_folder)
        alt_serial.triggered.connect(self.alter_list_serial)
        add_prefix.triggered.connect(self.add_list_prefix)
        add_postfix.triggered.connect(self.add_list_postfix)
        alt_prefix.triggered.connect(self.alter_list_prefix)
        alt_postfix.triggered.connect(self.alter_list_postfix)
        clear_list.triggered.connect(self.clear_list_files)

    def leaveEvent(self, event):
        self.signalMouseLeave.emit()
        start = self.model.index(0)
        stop = self.model.index(self.model.rowCount())
        self.model.dataChanged.emit(start, stop)

    def keyPressEvent(self, event):
        self.modifier = event.modifiers()

        # Catch 'arrow keys' to move upward or downward
        if event.key() == Qt.Key_Up or event.key() == Qt.Key_Down:
            # Validate 'self.previous'
            if self.previous < self.model().rowCount() - 1 and event.key() == Qt.Key_Down:
                self.previous += 1
            if self.previous > 0 and event.key() == Qt.Key_Up:
                self.previous -= 1
            
            self.chosen.clear()
            self.chosen.append(self.previous)
            self.signalMouseClick.emit(self.chosen)

            # Highlight the whole line
            start = self.model().index(self.previous, 0)
            self.model.dataChanged.emit(start, start)

        # Catch othewise keys here
        super().keyPressEvent(event)

    def mouseMoveEvent(self, event):
        hover = self.indexAt(event.position().toPoint())
        self.signalMouseHover.emit(hover.row())

        # Notify 'delegate' to update the hover line
        start = self.model.index(hover.row())
        self.model.dataChanged.emit(start, start)

    def mousePressEvent(self, event):
        self.modifier = event.modifiers()

        # Mouse Left Button Click
        if event.buttons() == Qt.MouseButton.LeftButton:
            clicked = self.indexAt(event.position().toPoint())
            # Not valid click
            if clicked.row() == -1:
                return
            model = clicked.model()
            self.previous = clicked.row()  

            # Qt.NoModifier
            if self.modifier == Qt.NoModifier:
                self.chosen.clear()
                self.recorder = clicked.row()
                self.previous = clicked.row()
                self.chosen.append(self.recorder)
                self.signalMouseClick.emit([self.recorder])
                # Notify 'delegate' to update the line
                start = model.index(clicked.row())
                model.dataChanged.emit(start, start)

            # Qt.ControlModifier
            elif self.modifier == Qt.ControlModifier:
                # first click with 'ctrl', set 'previous' to current line
                if self.recorder < 0:
                    self.recorder = clicked.row()

                # Add line clicked before 'ctrl' pressed
                if self.recorder not in self.chosen:
                    self.chosen.append(self.recorder)

                # Add current clicked line if not added 
                if clicked.row() not in self.chosen:
                    self.chosen.append(clicked.row())
                self.signalMouseClick.emit(self.chosen)

                # Notify 'delegate' to update the whole line
                start = min(self.chosen)
                stop = max(self.chosen)
                indexFrom = model.index(start)
                indexTo   = model.index(stop)
                model.dataChanged.emit(indexFrom, indexTo)

            # Qt.ShiftModifier
            elif self.modifier == Qt.ShiftModifier:
                # first click with 'shift', set 'previous' to current line
                if self.recorder < 0:
                    self.recorder = clicked.row()

                # If there are lines selected by 'ctrl', get the last line
                lowest = 0
                highest = 0
                if self.chosen:
                    self.recorder = self.chosen[-1]
                    lowest = min(self.chosen)
                    highest = max(self.chosen)
                    self.chosen.clear()

                larger = max(self.recorder, clicked.row())
                smaller = min(self.recorder, clicked.row())
                for line in range(smaller, larger+1):
                    if line not in self.chosen:
                        self.chosen.append(line)
                self.signalMouseClick.emit(self.chosen)

                # Notify 'delegate' to update
                start = min(lowest, smaller)
                stop  = max(highest, larger)
                indexFrom = model.index(start)
                indexTo   = model.index(stop)
                model.dataChanged.emit(indexFrom, indexTo)
        super().mousePressEvent(event)

    def update_list_folder(self):
        path = QFileDialog.getExistingDirectory(self, 'Select a directory to add', self.cwd)
        if not path:
            return

        self.cwd = path
        for file in os.listdir(self.cwd):
            if not file.endswith('fCal') and not file.endswith('pCal') and not file.endswith('pfCal'):
                continue
            self.assembler.append(os.path.abspath(file))
        self.model.listUpdate()

    def update_list_files(self):
        files, _ = QFileDialog.getOpenFileNames(self, "Select files to add", '.', '')
        if not files:
            return

        for file in files:
            if file not in self.assembler:
                self.assembler.append(file)
        self.model.listUpdate()

    def alter_list_serial(self):
        print("alter serial")

    def add_list_prefix(self):
        print("add prefix")
        if not self.chosen:
            return
        buffer = []
        for line in self.chosen:
            print("src filename: ", self.assembler[line])
            buffer.append(self.assembler[line])

        dialog = AddPrefixDialog(750, 450, buffer)
        dialog.exec()


    def add_list_postfix(self):
        print("add postfix")

    def alter_list_prefix(self):
        print("alter prefix")

    def alter_list_postfix(self):
        print("alter postfix")

    def clear_list_files(self):
        self.assembler.clear()
        self.model.listErase()

